;; Summation of primes

;; Problem 10

;; The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17.

;; Find the sum of all the primes below two million.


(import
 (except (rnrs base)
         let-values
         map)
 (only (guile)
       lambda* λ)
 (ice-9 futures)
 (lib rabin-miller-test)
 (lib naive-prime-test))


(define even?
  (λ (num)
    (= (remainder num 2) 0)))


(define next
  (λ (num)
    (cond
     [(even? num) (+ num 1)]
     [else (+ num 2)])))


(define sum-of-primes
  (λ (start limit)
    (let loop ([num start]
               [acc 0]
               [primes-count 1])
      (cond
       [(> num limit)
        (display (simple-format #f "number of primes: ~a\n" primes-count))
        acc]
       [(prime? num)
        (loop (next num)
              (+ acc num)
              (+ primes-count 1))]
       [else
        (loop (next num)
              acc
              primes-count)]))))


(define make-segment
  (λ (start end)
    (cons start end)))

(define segment-start
  (λ (seg)
    (car seg)))

(define segment-end
  (λ (seg)
    (cdr seg)))


(define segment
  (λ (start end segment-count)
    "Make segments of equal size, except for rounding to whole numbers, which might result in slightly more "
    (let ([segment-size
           (ceiling
            (/ (- end start)
               segment-count))])
      (let loop ([pos start])
        (cond
         [(>= (+ pos segment-size) end)
          (list (make-segment pos end))]
         [else
          (cons (make-segment pos (+ pos segment-size))
                (loop (next (+ pos segment-size))))])))))


(let* ([start 2]
       [limit (- (* 2 (expt 10 6)) 1)]
       [cores-count 32]
       [segments (segment start limit cores-count)])
  (let ([futures
         (map (λ (seg)
                (future
                 (sum-of-primes (segment-start seg)
                                (segment-end seg))))
              segments)])
    (apply + (map touch futures))))
